/* Copyright (c) 2021, LiWangQian<liwangqian@huawei.com> All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this list of
 *    conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
 *    of conditions and the following disclaimer in the documentation and/or other materials
 *    provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
 *    to endorse or promote products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#pragma once

#include <string>
#include <functional>
#include <memory>
#include <libcli/utils.h>

namespace libcli {

struct element_1 {
    ~element_1();
    element_1(const std::string_view &name, const std::string_view &desc);

    virtual bool match(const std::string &name) const;
    virtual bool check(const std::string &val) const;
    virtual bool is_key() const = 0;
    virtual std::string help(const std::string& anchor) const = 0;
    virtual bool action() = 0;

private:
    std::string name_;
    std::string desc_;
};


class element {
public:
    virtual ~element() = default;
    explicit element(const std::string &name)
        : name_{name}
    {}

    const std::string &name() const
    {
        return name_;
    }

    virtual bool match(std::string name) const
    {
        return (name_.length() >= name.length()) &&
            std::equal(name.begin(), name.end(), name_.begin());
    }

    virtual bool check(const std::string &val) const
    {
        return true;
    }

    virtual bool is_key() const = 0;

    virtual std::string help(const std::string& anchor) const = 0;

private:
    std::string name_;
};

using element_sptr = std::shared_ptr<element>;

class simple_element : public element {
public:
    virtual ~simple_element() = default;
    simple_element(const std::string &name, const std::string &desc)
        : element(name), desc_{desc}
    {}

    std::string help(const std::string&) const override
    {
        return desc_;
    }

private:
    std::string desc_;
};

class complex_element : public element {
public:
    using check_functor = std::function<bool(const std::string&)>;
    using help_functor = std::function<std::string(const std::string&)>;

    virtual ~complex_element() = default;
    complex_element(const std::string &name, check_functor checker, help_functor helper)
        : element(name) , checker_{checker} , helper_{helper}
    {}

    bool check(const std::string &val) const override
    {
        return checker_ && checker_(val);
    }

    std::string help(const std::string& anchor) const override
    {
        if (helper_) {
            return helper_(anchor);
        } else {
            return "";
        }
    }

private:
    check_functor checker_{nullptr};
    help_functor helper_{nullptr};
};

class key_element : public simple_element {
public:
    virtual ~key_element() = default;
    using simple_element::simple_element;

    bool is_key() const override { return true; }
};

template <typename SimpleOrComplexElement = simple_element>
class parameter_element : public SimpleOrComplexElement {
public:
    virtual ~parameter_element() = default;
    using SimpleOrComplexElement::SimpleOrComplexElement;

    bool is_key() const override { return false; }
};

} // namespace libcli
